package cn.thoughtworks.school.services;

import cn.thoughtworks.school.entities.*;
import cn.thoughtworks.school.exceptions.BusinessException;
import cn.thoughtworks.school.feign.OrganizationCenterFeign;
import cn.thoughtworks.school.repositories.SocialAccountRepository;
import cn.thoughtworks.school.repositories.UserDetailRepository;
import cn.thoughtworks.school.repositories.UserRepository;
import cn.thoughtworks.school.repositories.UserRoleRepository;
import cn.thoughtworks.school.utils.Encrypt;
import cn.thoughtworks.school.utils.JwtUtil;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserService {

  @Autowired
  private UserRepository userRepository;
  @Autowired
  private UserRoleRepository userRoleRepository;
  @Autowired
  private SocialAccountRepository socialAccountRepository;
  @Autowired
  private NodeBBUserService nodeBBUserService;
  @Autowired
  private UserDetailRepository userDetailRepository;
  @Autowired
  private OrganizationCenterFeign organizationCenterFeign;

  public User usernameLogin(User user) throws BusinessException {
    user.encryptPassword();
    User current = userRepository.findByUsernameOrEmailAndPassword(user.getUsername(), user.getPassword())
      .orElseThrow(() -> new BusinessException("Username email or password is invalid."));

    return current;
  }

  private List<Integer> getUserRoles(User current) {
    return userRoleRepository.findByUserId(current.getId())
      .stream().map(UserRole::getRole).collect(Collectors.toList());
  }

  public String wechatLogin(SocialAccount socialAccount) throws IOException {
    socialAccount = socialAccountRepository.findByOpenid(socialAccount.getOpenid());
    User user = userRepository.findByUsername(socialAccount.getUsername());

    return getJwt(user);
  }

  public SocialAccount saveSocialAccount(SocialAccount socialAccount) {
    return socialAccountRepository.save(socialAccount);
  }

  public SocialAccount findSocialAccountByOpenid(String openid) {
    return socialAccountRepository.findByOpenid(openid);
  }

  public SocialAccount findSocialAccountById(Long id) throws BusinessException {
    return socialAccountRepository.findById(id)
      .orElseThrow(() -> new BusinessException(String.format("Current social account id %s is not exist.", id)));
  }

  public void validDateUser(User user) throws BusinessException {
    isEmailExisted(user);
    isUserNameExisted(user);
  }

  private void isEmailExisted(User user) throws BusinessException {
    if (userRepository.getUserByEmail(user.getEmail()).size() != 0) {
      throw new BusinessException("邮箱已存在！");
    }
  }

  private void isUserNameExisted(User user) throws BusinessException {
    if (userRepository.getUserByUsername(user.getUsername()).size() != 0) {
      throw new BusinessException("用户名已存在！");
    }
  }

  public User createUser(CreateUserCommand userCommand) throws Exception {
    User user = userCommand.buildUser();
    UserDetail userDetail = new UserDetail();
    user.encryptPassword();
    User createUser = userRepository.save(user);
    userDetail.setUserId(createUser.getId());
    userDetail.setName(createUser.getUsername());
    if (hasOrganization(userCommand)) {
      Long organizationId = getOrganization(userCommand);
      userDetail.setCurrentOrganizationId(organizationId);
      addUserOrganization(createUser.getId(), organizationId);
    }
    userDetailRepository.save(userDetail);

    return createUser;
  }

  private Long getOrganization(CreateUserCommand userCommand) {
    return Long.valueOf(userCommand.getOrg().split("\\.")[2]);
  }

  //前端传过来的 orgArr = [hashcode(organizationId+enable),'',organizationId]
  private boolean hasOrganization(CreateUserCommand userCommand) {
    int enable = 1;
    String[] orgArr = Optional.ofNullable(userCommand.getOrg()).orElse("")
      .split("\\.");
    if (orgArr.length != 3) {
      return false;
    }
    return Encrypt.hashcode(orgArr[2] + enable).equals(orgArr[0]);
  }

  private void addUserOrganization(Long userId, Long organizationId) {
    Map userOrganization = new HashMap();
    userOrganization.put("userId", userId);
    userOrganization.put("organizationId", organizationId);
    organizationCenterFeign.addUsersToOrganization(userOrganization);
  }

  private boolean needToBindSocialAccount(SocialAccount socialAccount) {
    return Objects.isNull(socialAccount.getUsername());
  }

  public void dealBindSocialAccount(User createdUser, Long socialAccountId) {
    SocialAccount socialAccount = socialAccountRepository.findById(socialAccountId)
      .orElse(new SocialAccount());
    if (needToBindSocialAccount(socialAccount)) {
      socialAccount.setUsername(createdUser.getUsername());
      socialAccountRepository.save(socialAccount);
    }
  }

  public String getJwt(User user) throws UnsupportedEncodingException {
    List<Integer> userRoles = getUserRoles(user);
    user.setRoles(userRoles);
    JSONObject jsonObject = new JSONObject(user);
    return JwtUtil.build(jsonObject.toString());
  }

  public SocialAccount bindWechat(String username, SocialAccount wechatInfo) {
    SocialAccount socialAccount = findSocialAccountByOpenid(wechatInfo.getOpenid());

    if (nonExistSocialAccount(socialAccount)) {
      wechatInfo.setUsername(username);
      return saveSocialAccount(wechatInfo);
    }

    socialAccount.setUsername(username);
    return saveSocialAccount(socialAccount);
  }

  public boolean nonExistSocialAccount(SocialAccount socialAccount) {
    return Objects.isNull(socialAccount);
  }

  public void validParams(CreateUserCommand userCommand) throws BusinessException {
    if (Objects.isNull(userCommand.getCode()) && Objects.isNull(userCommand.getOrg())) {
      throw new BusinessException("Params is invalid. ");
    }
  }
}
